import { SubjectComponent } from "./subject";

const { ccclass, property, executeInEditMode } = cc._decorator;

@ccclass("AssetHandle")

export class AssetHandle {
    @property()
    name = "";
    @property()
    path = ""
    @property(cc.Asset)
    asset: cc.Asset = null;
    @property()
    group = "";

    equals(other: AssetHandle) {
        return this.name === other.name && this.path === other.path && this.asset == other.asset && this.group == other.group;
    }
};

@ccclass
@executeInEditMode
export default class AssetHandleNode extends SubjectComponent {
    protected _assetNameMap: Map<string, [AssetHandle]> = new Map();
    protected _assetTypeMap: Map<string, [AssetHandle]> = new Map();
    protected _assetGroupMap: Map<string, [AssetHandle]> = new Map();
    protected _assetPathMap: Map<string, AssetHandle> = new Map();
    @property([AssetHandle])
    handles: AssetHandle[] = [];

    get nameMap() {
        return this._assetNameMap;
    }
    get typeMap() {
        return this._assetTypeMap;
    }
    get groupMap() {
        return this._assetGroupMap;
    }
    get pathMap() {
        return this._assetPathMap;
    }

    protected onLoad() {
        this.remap();
    }
    remap() {
        this._assetNameMap.clear();
        this._assetTypeMap.clear();
        this._assetGroupMap.clear();
        this._assetPathMap.clear();
        this.handles.forEach(ah => this.remapOne(ah));
    }
    remapOne(ah: AssetHandle) {
        ah.name = ah.name || ah.asset.name;
        this.__assignCell(this._assetNameMap, ah.name, ah);
        if (ah.path) {
            this._assetPathMap.set(ah.path, ah);
        }
        let clsName = cc.js.getClassName(ah.asset);
        this.__assignCell(this._assetTypeMap, clsName, ah);
        this.__assignCell(this._assetGroupMap, ah.group, ah);
    }

    private __assignCell(map: Map<string, any>, name: string, value: any) {
        let cell = map.get(name);
        if (cell) {
            cell.push(value);
        }
        else {
            map.set(name, [value]);
        }
    }

    setAsset(ah: AssetHandle): void;
    setAsset(name: string, asset: cc.Asset): void;
    setAsset(nameOrAh: string | AssetHandle, asset?: cc.Asset): void {
        let ah: AssetHandle = null;
        if (typeof nameOrAh === "string") {
            ah = new AssetHandle();
            ah.name = nameOrAh;
            ah.asset = asset;
        }
        else {
            ah = nameOrAh;
        }

        let foundIndex = this.handles.findIndex(ele => ele.equals(ah));
        if (foundIndex === -1) {
            this.handles.push(ah); //添加资源
            this.remapOne(ah);
        }
    }

    getAssetByPath<T>(path: string) {
        return <T>this._assetPathMap.get(path)?.asset || null;
    }
    getAssetHandles(name: string, group: string, className: string) {
        if (!group && !name && !className) {
            return [];
        }
        let arrs: AssetHandle[][] = [];
        if (group) {
            let groupContact: AssetHandle[] = null;
            groupContact = this._assetGroupMap.get(group) || [];
            if (groupContact.length === 0) {
                return [];
            }
            arrs.push(groupContact);
        }

        if (name) {
            let nameContact: AssetHandle[] = null;
            nameContact = this._assetNameMap.get(name) || [];
            if (nameContact.length === 0) {
                return [];
            }
            arrs.push(nameContact);
        }

        if (className) {
            let typeContact: AssetHandle[] = null;
            typeContact = this._assetTypeMap.get(className);
            if (typeContact.length === 0) {
                return [];
            }
            arrs.push(typeContact);
        }

        let headArr = arrs.splice(0, 1)[0];
        let ahs = headArr.filter((v) => {
            for (let i = 0; i < arrs.length; i++) {
                if (!arrs[i].includes(v)) {
                    return false;
                }
            }
            return true;
        });

        return ahs;
    }
    getAssetHandlesByGroup(group: string) {

    }
    getAssets<T>(name: string = "", group: string = "", className: string = "") {
        let ahs = this.getAssetHandles(name, group, className);
        let assets: cc.Asset[] = [];
        ahs.forEach(ah => {
            assets.push(ah.asset);
        });

        return <T[]>assets;
    }
    getAsset<T>(name: string = "", group: string = "", className: string = "") {
        return <T>this.getAssets(name, group, className)[0] || null;
    }
};